﻿/** \file
* \$Rev: 107 $
* 
* \$Date: 2011-04-12 23:22:43 +0000 (Tue, 12 Apr 2011) $
*
* \$URL: http://benfwaves.googlecode.com/svn/branches/fftw/FFTW/AlignedArray.cs $
*/

using System;
using System.Runtime.InteropServices;

namespace FFTW
{
	/// <summary>
	/// A complex number.
	/// </summary>
	/// <typeparam name="ElementType">Float or double.</typeparam>
	[StructLayout(LayoutKind.Sequential)]
	public class Complex<ElementType>
		where ElementType : struct
	{
		/// <summary>The real component of the complex number.</summary>
		public ElementType real;
		/// <summary>The imaginary component of the complex number.</summary>
		public ElementType imag;

		/// <summary>The magnitude of the complex number.</summary>
		public ElementType Magnitude
		{
			get
			{
				return (ElementType)(object)ComplexMagnitude(
					(double)(object)real, (double)(object)imag);
			}
		}
		/// <summary>The phase angle of the complex number, in degrees.</summary>
		public ElementType Phase
		{
			get
			{
				return (ElementType)(object)ComplexPhase(
					(double)(object)real, (double)(object)imag);
			}
		}

		/// <summary>The constructor.</summary>
		public Complex() { }

		/// <summary>
		/// Calculate the magnitude of a complex number.
		/// </summary>
		/// <param name="x">The real component.</param>
		/// <param name="y">The imaginary component.</param>
		/// <returns>The magnitude.</returns>
		public static double ComplexMagnitude(double x, double y)
		{
			return Math.Sqrt(x * x + y * y);
		}

		/// <summary>
		/// Calculate the phase angle of a complex number.
		/// </summary>
		/// <param name="x">The real component.</param>
		/// <param name="y">The imaginary component.</param>
		/// <returns>The phase angle, in degrees.</returns>
		public static double ComplexPhase(double x, double y)
		{
			return 180 / Math.PI * Math.Atan2(x, y);
		}
	}

	/// <summary>
	/// The base class for all FFTW arrays. The memory is allocated and freed
	/// by FFTW and is aligned for improved performance.
	/// </summary>
	/// <typeparam name="BaseElementType">Double or float.</typeparam>
	/// <typeparam name="ElementType">Double, float, or complex.</typeparam>
	public abstract class AbstractAlignedArray<BaseElementType, ElementType> : SafeHandle
		where BaseElementType : struct
		where ElementType : new()
	{
		/// <summary>The number of elements in the array.</summary>
		public readonly int count;
		/// <summary>The size of each array element.</summary>
		public abstract int ElementSize { get; }

		/// <summary>The constructor.</summary>
		/// <param name="count">The number of elements to allocate.</param>
		public AbstractAlignedArray(int count)
			: base(IntPtr.Zero, true)
		{
			this.count = count;
			int bytesize = count * ElementSize;
			handle = PInvoke.malloc.GetDel<BaseElementType>()(bytesize);
		}

		/// <summary>
		/// Whether the safe handle to the array object is invalid.
		/// </summary>
		public override bool IsInvalid
		{
			get { return handle == IntPtr.Zero; }
		}

		/// <summary>
		/// Copy a managed array to the FFTW-owned array.
		/// </summary>
		/// <param name="dest">The managed array to copy.</param>
		public virtual void CopyTo(ElementType[] dest)
		{
			long p = handle.ToInt64(),
				elmsize = ElementSize;
			for (int i = 0; i < count; i++, p += elmsize)
			{
				if (dest[i] == null)
					dest[i] = new ElementType();
				Marshal.PtrToStructure(new IntPtr(p), dest[i]);
			}
		}

		/// <summary>
		/// Copy the FFTW-owned array to a managed array.
		/// </summary>
		/// <param name="source">The managed array to write to.</param>
		public virtual void CopyFrom(ElementType[] source)
		{
			long p = handle.ToInt64(),
				elmsize = ElementSize;
			for (int i = 0; i < count; i++, p += elmsize)
				Marshal.StructureToPtr(source[i], new IntPtr(p), false);
		}

		/// <summary>
		/// Gets a managed array copy (or copies a managed array).
		/// </summary>
		public ElementType[] Array
		{
			get
			{
				ElementType[] copied = new ElementType[count];
				CopyTo(copied);
				return copied;
			}
			set { CopyFrom(value); }
		}

		/// <summary>Destroys the array.</summary>
		/// <returns>Whether the array was successfully destroyed.</returns>
		protected override bool ReleaseHandle()
		{
			PInvoke.free.GetDel<BaseElementType>()(handle);
			return true;
		}
	}

	/// <summary>
	/// An aligned array of real data.
	/// </summary>
	/// <typeparam name="BaseElementType">Double or float.</typeparam>
	public class AlignedArrayReal<BaseElementType>
		: AbstractAlignedArray<BaseElementType, BaseElementType>
		where BaseElementType : struct
	{
		/// <summary>The constructor.</summary>
		/// <param name="count">The number of real elements to allocate.</param>
		public AlignedArrayReal(int count) : base(count) { }

		/// <summary>The size of each array element.</summary>
		public override int ElementSize
		{
			get
			{
				if (typeof(BaseElementType) == typeof(double))
					return Marshal.SizeOf(typeof(double));
				else if (typeof(BaseElementType) == typeof(float))
					return Marshal.SizeOf(typeof(float));
				else throw new ArgumentException();
			}
		}

		/// <summary>
		/// Copy the FFTW-owned array to a managed array.
		/// </summary>
		/// <param name="source">The managed array to write to.</param>
		public override void CopyFrom(BaseElementType[] source)
		{
			if (typeof(BaseElementType) == typeof(float))
				Marshal.Copy((float[])(object)source, 0, handle, source.Length);
			else if (typeof(BaseElementType) == typeof(double))
				Marshal.Copy((double[])(object)source, 0, handle, source.Length);
			else
				throw new ArgumentException();
		}

		/// <summary>
		/// Copy a managed array to the FFTW-owned array.
		/// </summary>
		/// <param name="dest">The managed array to copy.</param>
		public override void CopyTo(BaseElementType[] dest)
		{
			if (typeof(BaseElementType) == typeof(float))
				Marshal.Copy(handle, (float[])(object)dest, 0, dest.Length);
			else if (typeof(BaseElementType) == typeof(double))
				Marshal.Copy(handle, (double[])(object)dest, 0, dest.Length);
			else
				throw new ArgumentException();
		}
	}

	/// <summary>
	/// An aligned array of complex data.
	/// </summary>
	/// <typeparam name="BaseElementType">Double or float.</typeparam>
	public class AlignedArrayComplex<BaseElementType>
		: AbstractAlignedArray<BaseElementType, Complex<BaseElementType>>
		where BaseElementType : struct
	{
		/// <summary>The constructor.</summary>
		/// <param name="count">The number of real elements to allocate.</param>
		public AlignedArrayComplex(int count) : base(count) { }

		/// <summary>The size of each array element.</summary>
		public override int ElementSize
		{
			get
			{
				if (typeof(BaseElementType) == typeof(double))
					return 2*Marshal.SizeOf(typeof(double));
				else if (typeof(BaseElementType) == typeof(float))
					return 2*Marshal.SizeOf(typeof(float));
				else throw new ArgumentException();
			}
		}
	}
}
